package drds.server.sqlparser.parser;

import java.util.List;

import drds.server.sqlparser.sqlobject.expression.AggregateFunction;
import drds.server.sqlparser.sqlobject.expression.Expression;
import drds.server.sqlparser.sqlobject.expression.binary.BinaryExpression;
import drds.server.sqlparser.sqlobject.expression.binary.BinaryOperator;
import drds.server.sqlparser.sqlobject.expression.name.IName;
import drds.server.sqlparser.sqlobject.statement.query.GroupBy;
import drds.server.sqlparser.sqlobject.statement.query.IQuery;
import drds.server.sqlparser.sqlobject.statement.query.Limit;
import drds.server.sqlparser.sqlobject.statement.query.Query;
import drds.server.sqlparser.sqlobject.statement.query.SelectItem;
import drds.server.sqlparser.sqlobject.statement.query.SubQuery;
import drds.server.sqlparser.sqlobject.statement.query.order_by.OrderBy;
import drds.server.sqlparser.sqlobject.statement.query.order_by.OrderByItem;
import drds.server.sqlparser.sqlobject.statement.query.order_by.OrderByType;
import drds.server.sqlparser.sqlobject.statement.query.union_query.UnionOperator;
import drds.server.sqlparser.sqlobject.statement.query.union_query.UnionQuery;
import drds.server.sqlparser.sqlobject.statement.table.ExpressionTable;
import drds.server.sqlparser.sqlobject.statement.table.ITable;
import drds.server.sqlparser.sqlobject.statement.table.JoinTable;

/**
 * 
 * @author 曹正辉<330937205@qq.com>
 * 
 */
public class SelectParser extends SqlParser {

	protected ExpressionParser expressionParser;

	public SelectParser(String sql) {
		super(sql);
	}

	public SelectParser(Lexer lexer) {
		super(lexer);
		this.expressionParser = new ExpressionParser(lexer);
	}

	public SelectParser(ExpressionParser expressionParser) {
		super(expressionParser.getLexer());
		this.expressionParser = expressionParser;
	}

	/**
	 * 用于解析子查询
	 */
	public SubQuery parseSubQuery() {
		SubQuery subQuery = new SubQuery();
		subQuery.setQuery(parseQuery());
		return subQuery;
	}

	public IQuery parseQuery() {

		/**
		 * <pre>
		 * (select x,y,z from table order by limit ) union (all) (select x,y,z from table order by limit )
		 * </pre>
		 */
		if (lexer.getMark() == Mark.LEFT_PAREN) {
			lexer.nextMark();
			IQuery query = parseQuery();
			accept(Mark.RIGHT_PAREN);

			return parseQueryRest(query);
		}
		// 先走完一条流程
		Query query = new Query();
		accept(Mark.SELECT);
		if (lexer.getMark() == (Mark.DISTINCT)) {
			query.setQuantifier(Query.DISTINCT);
			lexer.nextMark();
		}
		parseSelectItemList(query);
		parseFrom(query);
		parseWhere(query);
		parseGroupBy(query);
		parseOrderBy(query);
		parseLimit(query);
		if (lexer.getMark() == Mark.FOR) {
			lexer.nextMark();
			accept(Mark.UPDATE);
			query.setForUpdate(true);
		}
		return parseQueryRest(query);
	}

	public IQuery parseQueryRest(IQuery query) {
		if (lexer.getMark() == Mark.UNION) {
			lexer.nextMark();
			UnionQuery unionQuery = new UnionQuery();
			unionQuery.setLeft(query);
			if (lexer.getMark() == Mark.ALL) {
				unionQuery.setUnionOperator(UnionOperator.UNION_ALL);
				lexer.nextMark();
			} else if (lexer.getMark() == Mark.DISTINCT) {
				unionQuery.setUnionOperator(UnionOperator.DISTINCT);
				lexer.nextMark();
			}
			lexer.check(Mark.LEFT_PAREN);// 必须(
			IQuery right = this.parseQuery();// select x,y,z from table where
			unionQuery.setRight(right);
			return unionQuery;
		} else {
			return query;
		}

	}

	protected void parseSelectItemList(Query query) {
		final List<SelectItem> selectItemList = query.getSelectItemList();
		for (;;) {
			lexer.nextMark();
			final SelectItem selectItem = parseSelectItem();
			selectItemList.add(selectItem);
			selectItem.setParent(query);

			if (lexer.getMark() != Mark.COMMA) {
				break;
			}
		}
	}

	public void parseFrom(Query query) {
		lexer.check(Mark.FROM);
		lexer.nextMark();
		query.setFrom(parseTable());
	}

	public ITable parseTable() {
		//
		lexer.check(Mark.IDENTIFIER);
		ExpressionTable expressionTable = new ExpressionTable();
		expressionTable.setExpression(this.expressionParser.parseName());
		if (lexer.mark == Mark.AS) {
			String alias = this.expressionParser.parseAs();
			if (alias == null) {
				throw new NullPointerException("alias");
			} else {
				expressionTable.setAlias(alias);
			}
		}

		ITable table = parseTableRest(expressionTable);
		return table;
	}

	public Expression parseExpression() {
		return this.expressionParser.parseExpression();
	}

	protected ITable parseTableRest(ITable table) {
		JoinTable.JoinType joinType = null;
		if (lexer.getMark() == Mark.LEFT) {
			lexer.nextMark();
			accept(Mark.JOIN);
			joinType = JoinTable.JoinType.LEFT_JOIN;
		} else if (lexer.getMark() == Mark.RIGHT) {
			lexer.nextMark();
			accept(Mark.JOIN);
			joinType = JoinTable.JoinType.RIGHT_JOIN;
		} else if (lexer.getMark() == Mark.JOIN) {
			joinType = JoinTable.JoinType.JOIN;
		}

		if (joinType != null) {
			lexer.nextMark();// 上面判断之后没有获取下一个关键字
			JoinTable joinTable = new JoinTable();
			joinTable.setLeft(table);
			joinTable.setJoinType(joinType);
			joinTable.setRight(parseTable());
			if (lexer.getMark() == Mark.ON) {
				joinTable.setOn(parseJoinOnExpression());
			}
			return parseTableRest(joinTable);// 可能含有别名和其他join表
		}

		return table;
	}

	/**
	 * 支持join on x.id=xx.id and x.name=yy.name 这种语法
	 */
	private Expression parseJoinOnExpression() {
		lexer.check(Mark.ON);
		Expression expression = null;
		boolean isFirst = true;
		do {
			lexer.nextMark();
			IName left = this.expressionParser.parseName();
			lexer.check(Mark.Equal);
			lexer.nextMark();
			IName right = this.expressionParser.parseName();
			BinaryExpression binaryExpression = new BinaryExpression(left, BinaryOperator.Equal, right);

			if (isFirst) {
				expression = binaryExpression;
				isFirst = false;
			} else {
				expression = new BinaryExpression(expression, BinaryOperator.And, binaryExpression);
			}
		} while (lexer.mark == Mark.AND);
		return expression;
	}

	protected void parseWhere(Query query) {
		if (lexer.getMark() == Mark.WHERE) {
			lexer.nextMark();
			/**
			 * 需要先过滤掉(where 1=1 and ... )[where 1=1 and] or (where 1=1 )[1=1]
			 */
			if (lexer.getMark() == Mark.INT) {
				lexer.nextMark();
				lexer.check(Mark.Equal);
				lexer.nextMark();
				lexer.check(Mark.INT);
				lexer.nextMark();// 可能是and 也可能是字段名
				if (lexer.getMark() == Mark.AND) {
					lexer.nextMark();
				} else if (lexer.getMark() == Mark.GROUP_BY) {
					return;
				} else if (lexer.getMark() == Mark.ORDER_BY) {
					return;
				} else if (lexer.getMark() == Mark.EOF) {
					return;
				}
			}
			// start IDENTIFIER
			Expression where = parseExpression();
			query.setWhere(where);
		}
	}

	/**
	 * 简单
	 */
	public SelectItem parseSelectItem() {
		/**
		 * name有两种形式:a a.b
		 */
		// count sum avg
		Expression expression = null;
		if (lexer.mark == Mark.IDENTIFIER) {
			expression = this.expressionParser.parseName();
		} else {
			if (lexer.mark == Mark.MIN || //
					lexer.mark == Mark.MAX || //
					lexer.mark == Mark.COUNT || //
					lexer.mark == Mark.SUM || //
					lexer.mark == Mark.AVG) {//
				AggregateFunction aggregateFunction = new AggregateFunction(lexer.mark.toString());
				lexer.nextMark();
				lexer.check(Mark.LEFT_PAREN);
				lexer.nextMark();
				aggregateFunction.getArgumentList().add(this.expressionParser.parseName());
				lexer.check(Mark.RIGHT_PAREN);
				lexer.nextMark();
				expression = aggregateFunction;
			} else {
				throw new RuntimeException("to do");
			}
		}
		final String alias = this.expressionParser.parseAs();
		return new SelectItem(expression, alias);
	}

	protected void parseGroupBy(Query query) {
		if (lexer.getMark() == (Mark.GROUP_BY)) {
			lexer.nextMark();

			GroupBy groupBy = new GroupBy();
			for (;;) {
				Expression item = parseGroupByItem();
				item.setParent(groupBy);
				groupBy.addGroupByItem(item);

				if (!(lexer.getMark() == (Mark.COMMA))) {
					break;
				}

				lexer.nextMark();
			}

			if (lexer.getMark() == (Mark.GROUP_BY_AND)) {

				Expression filter = parseGroupByFilter();
				groupBy.setFilter(filter);
			}

			query.setGroupBy(groupBy);
		}
	}

	protected Expression parseGroupByItem() {
		// 要么group by a 要么 group by a.b
		Expression item = this.expressionParser.parseName();
		return item;
	}

	// having count(x,x.y)>=0 or like where 条件
	public Expression parseGroupByFilter() {
		lexer.check(Mark.GROUP_BY_AND);
		lexer.nextMark();
		if (!(Mark.MIN == lexer.mark || Mark.MAX == lexer.mark || Mark.SUM == lexer.mark || Mark.COUNT == lexer.mark || Mark.AVG == lexer.mark)) {
			throw new IllegalArgumentException("只支持聚合函数");
		}
		String methodName = lexer.stringValue;
		if (!this.expressionParser.isAggreateFunction(methodName)) {
			throw new NullPointerException("不是聚合函数");
		}
		AggregateFunction aggregateFunction = this.expressionParser.parseAggregateFunction(methodName);
		// 支持> >= < <= = != 整数/小数/?

		/**
		 * case INT: return new Int
		 * 
		 * return new Decimal Variable
		 */
		lexer.nextMark();
		if (lexer.getMark() == Mark.Equal) {
			lexer.nextMark();
			Expression right = this.expressionParser.value();
			return new BinaryExpression(aggregateFunction, BinaryOperator.Equal, right);
		} else if (lexer.getMark() == Mark.NotEqual) {
			lexer.nextMark();
			Expression right = this.expressionParser.value();
			return new BinaryExpression(aggregateFunction, BinaryOperator.NotEqual, right);
		} else if (lexer.getMark() == Mark.LessThan) {
			BinaryOperator binaryOperator = BinaryOperator.LessThan;
			lexer.nextMark();
			if (lexer.getMark() == Mark.Equal) {
				lexer.nextMark();
				binaryOperator = BinaryOperator.LessThanOrEqual;
			}
			Expression right = this.expressionParser.value();
			return new BinaryExpression(aggregateFunction, binaryOperator, right);
		} else if (lexer.getMark() == Mark.LessThanOrEqual) {
			lexer.nextMark();
			Expression right = this.expressionParser.value();
			return new BinaryExpression(aggregateFunction, BinaryOperator.LessThanOrEqual, right);
		} else if (lexer.getMark() == Mark.GreaterThan) {
			BinaryOperator operator = BinaryOperator.GreaterThan;
			lexer.nextMark();
			if (lexer.getMark() == Mark.Equal) {
				lexer.nextMark();
				operator = BinaryOperator.GreaterThanOrEqual;
			}
			Expression right = this.expressionParser.value();
			return new BinaryExpression(aggregateFunction, operator, right);
		} else if (lexer.getMark() == Mark.GreaterThanOrEqual) {
			lexer.nextMark();
			Expression right = this.expressionParser.value();
			return new BinaryExpression(aggregateFunction, BinaryOperator.GreaterThanOrEqual, right);
		} else {
			throw new IllegalArgumentException("else");
		}
	}

	public void parseOrderBy(Query query) {
		if (lexer.getMark() == Mark.ORDER_BY) {
			OrderBy orderBy = new OrderBy();
			lexer.nextMark();
			orderBy.addOrderByItem(parseOrderByItem());

			while (lexer.getMark() == Mark.COMMA) {
				lexer.nextMark();
				orderBy.addOrderByItem(parseOrderByItem());
			}
			query.setOrderBy(orderBy);
		}
	}

	public OrderByItem parseOrderByItem() {
		OrderByItem orderByItem = new OrderByItem();

		orderByItem.setExpression(parseExpression());

		if (lexer.getMark() == Mark.ASC) {
			lexer.nextMark();
			orderByItem.setOrderByType(OrderByType.ASC);
		} else if (lexer.getMark() == Mark.DESC) {
			lexer.nextMark();
			orderByItem.setOrderByType(OrderByType.DESC);
		} else {
			throw new RuntimeException("else...");
		}

		return orderByItem;
	}

	public void parseLimit(Query query) {
		if (lexer.getMark() == Mark.LIMIT) {
			lexer.nextMark();

			Limit limit = new Limit();

			Expression temp = this.parseExpression();
			if (lexer.getMark() != (Mark.COMMA)) {
				throw new IllegalArgumentException("must limit x,y");
			} else {

				limit.setOffset(temp);
				lexer.nextMark();
				limit.setCount(this.parseExpression());

			}
			query.setLimit(limit);
		}

	}

}
